package com.itheima.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.itheima.goods.pojo.GoodsInfo;
import com.itheima.search.service.SearchService;
import org.apache.lucene.search.TotalHits;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author wang
 * @description
 * @date 2021/7/31 10:33
 */
@Service("searchService")
public class SearchServiceImpl implements SearchService {
    @Qualifier("restHighLevelClient")
    @Autowired
    RestHighLevelClient client;

    @Override
    public Map search(Map searchMap) {
        //0.结果集
        Map resultMap = new HashMap();
        //1.1 检查searchMap是否有数据
        if (searchMap == null || searchMap.isEmpty()) {
            return resultMap;
        }
        //1.2 检查参数 keyword关键字是必传项，为空直接返回
        String keyword = (String) searchMap.get("keyword");
        if (keyword == null || "".equals(keyword.trim())) {
            return resultMap;
        }
        //2. 查询和此关键字有关的所有分类和品牌列表
        Map brandsAndCategorys = null;
        Map goods = null;
        try {
            brandsAndCategorys = selectBrandAndCategory(keyword);
            goods = selectGoods(searchMap);
        } catch (IOException e) {
            e.printStackTrace();
        }
        resultMap.putAll(brandsAndCategorys);
        //3. 查询满足条件商品数据
        resultMap.putAll(goods);
        //4.返回结果集
        return resultMap;
    }

    private Map selectBrandAndCategory(String keyword) throws IOException {
        //2.0 创建结果集
        Map<String, List<String>> brandsAndCategorys = new HashMap<>();
        //2.1 创建ES搜索请求
        SearchRequest request = new SearchRequest("goodsinfo");//查询的索引表
        //2.1.1创建查询请求对象
        SearchSourceBuilder builder = new SearchSourceBuilder();
        //2.1.2构建查询语义
        //设置查询条件 , 按照keyword进行分词查询 (商品标题字段)
        //设置聚合条件 , 按照品牌进行聚合分桶
        //设置聚合条件 , 按照分类进行聚合分桶
        builder.query(QueryBuilders.matchQuery("name", keyword))
                .size(0)
                .aggregation(AggregationBuilders.terms("brandList")
                        .field("brandName")
                        .size(Integer.MAX_VALUE))
                .aggregation(AggregationBuilders.terms("categoryList")
                        .field("categoryName")
                        .size(Integer.MAX_VALUE));
        //2.1.3发送请求,获得响应结果
        request.source(builder);
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //2.2 解析响应结果
        Aggregations aggregations = response.getAggregations();
        //获取聚合分桶结果
        for (Aggregation aggregation : aggregations) {
            ParsedStringTerms st = (ParsedStringTerms) aggregation;
            List<String> results = new ArrayList<String>();
            List<? extends Terms.Bucket> buckets = st.getBuckets();
            if (buckets != null) {
                for (Terms.Bucket bucket : buckets) {
                    results.add(bucket.getKeyAsString());
                }
            }
            brandsAndCategorys.put(st.getName(), results);
        }
        return brandsAndCategorys;
    }

    private Map selectGoods(Map map) throws IOException {
        //3.0 创建结果集,map.get("brand")
        Map<String, Object> selectGoods = new HashMap<>();
        //3.1 创建ES搜索请求
        SearchRequest request = new SearchRequest("goodsinfo");//查询的索引表
        //3.1.1创建查询请求对象
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //3.1.2设置查询条件 , 多条件通过bool布尔条件组合
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();//组装条件,涉及到多个条件, 使用布尔查询
        //must: 按照keyword进行分词查询 (商品标题字段)
        boolQueryBuilder.must(QueryBuilders.matchQuery("name", map.get("keyword")));
        //filter: 如果品牌brand不为空，词条查询(品牌字段)
        String brand = (String) map.get("brand");
        if (brand != null && !"".equals(brand.trim())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("brandName", map.get("brand")));
        }
        //filter: 如果分类category不为空，词条查询(分类字段)
        String category = (String) map.get("category");
        if (category != null && !"".equals(category.trim())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("categoryName", map.get("category")));
        }
        //filter: 如果价格price不为空，按照"-"切割字符串 ，进行范围查询
        String price = (String) map.get("price");
        if (price != null && !"".equals(price.trim())) {
            String[] prices = price.split("-");
            int lowerPrice = Integer.parseInt(prices[0]);
            boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(lowerPrice));
            if (prices.length == 2) {
                int higherPrice = Integer.parseInt(prices[1]);
                boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").lte(higherPrice));
            }
        }
        //3.1.3将布尔查询条件放入查询请求对象
        sourceBuilder.query(boolQueryBuilder);
        //3.1.4设置排序条件
        //如果sortField字符不为空，按照该字段排序
        //sortRule代表升序(ASC) 降序(DESC)
        String sortField = (String) map.get("sortField");
        String sortRule = (String) map.get("sortRule");
        if (sortField != null && !"".equals(sortField.trim())
                && sortRule != null && !"".equals(sortRule.trim())) {
            if ("ASC".equalsIgnoreCase(sortRule)) {
                //升序
                sourceBuilder.sort(SortBuilders.fieldSort(sortField).order(SortOrder.ASC));
            } else {
                //降序
                sourceBuilder.sort(SortBuilders.fieldSort(sortField).order(SortOrder.DESC));
            }
        }
        //3.1.5设置分页条件
        int pageNum = Integer.parseInt(String.valueOf(map.get("pageNum"))); //当前页
        int pageSize = Integer.parseInt(String.valueOf(map.get("pageSize"))); //每页显示多少条
        //按照pageNum ,pageSize进行分页
        if (pageNum < 1) {
            pageNum = 1;
        }
        if (pageSize < 1) {
            pageSize = 10;
        }
        sourceBuilder.from((pageNum - 1) * pageSize);
        sourceBuilder.size(pageSize);
        //3.1.6设置高亮条件
        sourceBuilder.highlighter(new HighlightBuilder().field("name"));
        //3.1.7将查询条件放入
        request.source(sourceBuilder);
        //3.1.8发送请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //3.2 解析响应结果
        //查询满足条件总数量: total
        SearchHits searchHits = response.getHits();
        TotalHits totalHits = searchHits.getTotalHits();
        Long total = totalHits.value;
        //查询一共有多少页: pages
        List<GoodsInfo> goodsInfoList = new ArrayList<GoodsInfo>();
        SearchHit[] hits = searchHits.getHits();
        //查询当前页商品集合: goodList
        for (SearchHit hit : hits) {
            //获取原始结果
            String source = hit.getSourceAsString();
            GoodsInfo goodsInfo = JSON.parseObject(source, GoodsInfo.class);
            //获取高亮结果, 并解析
            Map<String, HighlightField> highlightFieldMap = hit.getHighlightFields();
            if (highlightFieldMap != null) {
                HighlightField highlightField = highlightFieldMap.get("name");
                if (highlightField != null) {
                    //设置名字为高亮字段
                    String highlightName = highlightField.getFragments()[0].toString();
                    goodsInfo.setName(highlightName);
                }
            }
            goodsInfoList.add(goodsInfo);
        }
        //封装数据至Map集合
        selectGoods.put("pageNum", pageNum);
        selectGoods.put("pageSize", pageSize);
        selectGoods.put("total", total);
        selectGoods.put("goodList", goodsInfoList);
        selectGoods.put("pages", (total + pageSize - 1) / pageSize);
        return selectGoods;
    }
}
